﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.Xml.Xsl;
using System.Runtime.InteropServices;

namespace GIVisualEditor
{
    public partial class GIVisualEditor : Form
    {
        private PropertyGrid propertyGrid = new PropertyGrid();
        private DirectedGraph graph = null;
        public static Dictionary<string, int> fileIconTable = new Dictionary<string, int>();


        public GIVisualEditor()
        {
            InitializeComponent();
            graph = new DirectedGraph();
        }




        public GIVisualEditor(string sFilePath)
        {
            InitializeComponent();
            graph = new DirectedGraph();
            ImportGraph(sFilePath, ref graph, false);
            
            
        }

        private void AddNodeToTree(DirectoryInfo oDir, TreeNodeCollection oNodeCollection)
        {
            TreeNode oNode = new TreeNode(oDir.Name, 0, 1);
            oNodeCollection.Add(oNode);

            // Now time to add templates files in this folder
            FileInfo[] oFiles = oDir.GetFiles();
            if (oFiles != null && oFiles.Length > 0)
            {
                foreach (FileInfo F in oFiles)
                {
                    int iFileImageIndex = 0;
                    string sKey = F.Extension.ToLower(); // lets check if we already have the icon image of this file
                    if (fileIconTable.ContainsKey(sKey))
                    {
                        iFileImageIndex = fileIconTable[sKey];
                    }
                    else
                    {
                        SHFILEINFO shinfo = new SHFILEINFO();

                        // Lets extract the file icon & put it in our map table
                        //Use this to get the small Icon
                        IntPtr hImgSmall = Win32Helper.SHGetFileInfo( F.FullName, 0, ref shinfo,
                                                       (uint)Marshal.SizeOf(shinfo),
                                                        Win32Helper.SHGFI_ICON |
                                                        Win32Helper.SHGFI_SMALLICON);

                        //The icon is returned in the hIcon member of the shinfo
                        //struct
                        System.Drawing.Icon fileIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);
                        iFileImageIndex = imageListTemplates.Images.Count;
                        fileIconTable.Add(sKey, iFileImageIndex );
                        imageListTemplates.Images.Add(fileIcon);
                    }

                    TreeNode childNode = new TreeNode(F.Name, iFileImageIndex, iFileImageIndex);
                    childNode.Tag = F.FullName;
                    oNode.Nodes.Add(childNode);
                }
            }

            // Time to look into sub directories
            DirectoryInfo[] oSubDirs = oDir.GetDirectories();
            foreach (DirectoryInfo dir in oSubDirs)
            {
                AddNodeToTree(dir, oNode.Nodes);
            }
 
        }

        private void FillTemplates()
        {
            string sTemplateFolderPath = string.Format("{0}Templates", frmMain.sThisAssemblyFolderPath);
            DirectoryInfo oDir = new DirectoryInfo(sTemplateFolderPath);
            if (oDir.Exists)
            {
                DirectoryInfo[] oSubDirs = oDir.GetDirectories();
                foreach (DirectoryInfo dir in oSubDirs)
                {
                    AddNodeToTree(dir, treeViewTemplates.Nodes);
                }
            }
            else
            {
                MessageBox.Show("Templates folder not found... looks like your setup was not correct.");
            }
        }

        public void ImportGraph(string sFilePath, ref DirectedGraph targetGraph, bool autoArrageUI)
        {
            int i = 0;
            XmlDocument oDoc = new XmlDocument();
            oDoc.Load(sFilePath);

            // Load the nodes first
            XmlNodeList oNodes = oDoc.SelectNodes("/GIVisualMachine/Nodes/Node");
            if (oNodes != null && oNodes.Count > 0)
            {
                for (i = 0; i < oNodes.Count; i++)
                {
                    Node n = new Node(targetGraph);
                    n.AllowStopHere = oNodes[i].SelectSingleNode("IsAcceptable").InnerText.ToLower() == "true";
                    n.IsCaseSensitive = oNodes[i].SelectSingleNode("IsCaseSensitive").InnerText.ToLower() == "true";
                    n.IsLiteral = oNodes[i].SelectSingleNode("IsLiteral").InnerText.ToLower() == "true";
                    n.IsStartNode = oNodes[i].SelectSingleNode("IsStartState").InnerText.ToLower() == "true";
                    n.Name = oNodes[i].SelectSingleNode("Name").InnerText;
                    n.Pattern = oNodes[i].SelectSingleNode("Pattern").InnerText;

                    if (autoArrageUI)
                    {
                        // TODO:


                    }
                    else
                    {
                        n.View.Left = int.Parse(oNodes[i].SelectSingleNode("UIPosition/Left").InnerText);
                        n.View.Top = int.Parse(oNodes[i].SelectSingleNode("UIPosition/Top").InnerText);
                    }

                    n.View.GotFocus += new EventHandler(UserSelectedObject);
                    targetGraph.Nodes.Add(n);

                    this.splitContainer1.Panel1.Controls.Add(n.View);
                }

                XmlNodeList oEdges = oDoc.SelectNodes("/GIVisualMachine/Edges/Edge");
                if (oEdges != null && oEdges.Count > 0)
                {
                    for (i = 0; i < oEdges.Count; i++)
                    {
                        Node N1 = targetGraph.Nodes[int.Parse(oEdges[i].SelectSingleNode("FromNode").InnerText)],
                             N2 = targetGraph.Nodes[int.Parse(oEdges[i].SelectSingleNode("ToNode").InnerText)];
                        Edge edge = new Edge(targetGraph, N1, N2);
                        targetGraph.Edges.Add(edge);
                        edge.View.GotFocus += new EventHandler(UserSelectedObject);
                        this.splitContainer1.Panel1.Controls.Add(edge.View);
                    }
                }
            }

            // Can edges exists without nodes ? ... na !
        }

        void UserSelectedObject(object sender, EventArgs e)
        {
            if (sender is StateViewControl)
            {
                StateViewControl oView = sender as StateViewControl;
                if (oView.AssociatedNode != null)
                {
                    this.propertyGrid.SelectedObject = oView.AssociatedNode;
                }
            } else
                if (sender is StateConnector)
                {
                    StateConnector oConnector = sender as StateConnector;
                    if (oConnector.AssociatedEdge != null)
                    {
                        this.propertyGrid.SelectedObject = oConnector.AssociatedEdge;
                    }

                }

        }

        private void GIVisualEditor_Load(object sender, EventArgs e)
        {
            toolTip1.SetToolTip(pictureBoxOnlineTemplates, "Checkout online templates");
           // toolTip1.SetToolTip(pictureBoxRefresh, "Refresh template view");

            toolTip2.IsBalloon = true;
            toolTip2.InitialDelay = 10;
            toolTip2.ReshowDelay = 1000;
            toolTip2.ShowAlways = true;

            toolTip2.SetToolTip(treeViewTemplates, "Templates feature is incomplete.\nPlease wait for next release.\nYou'll be notified automatically when the upgrade is available.");





            propertyGrid.Dock = DockStyle.Fill;
            splitContainer2.Panel2.Controls.Add(propertyGrid);
            FillTemplates();
            treeViewTemplates.NodeMouseDoubleClick += new TreeNodeMouseClickEventHandler(treeViewTemplates_NodeMouseDoubleClick);
        }

        void treeViewTemplates_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (treeViewTemplates.SelectedNode != null && treeViewTemplates.SelectedNode.Tag != null)
            {
                ImportGraph(treeViewTemplates.SelectedNode.Tag.ToString(), ref graph, true);
            }
        }

        private void addStateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            graph.IsDirty = true;
            Point where = this.splitContainer1.Panel1.PointToClient(Cursor.Position);
            if (where != null)
            {
                int x = where.X, y = where.Y;
                Node n = new Node(graph);
                if (graph.Nodes.Count == 0) n.IsStartNode = true;
                n.Left = x; n.Top = y; 
                n.Pattern = "State" + graph.Nodes.Count; n.IsLiteral = true;
                graph.Nodes.Add(n);
                n.View.GotFocus += new EventHandler(UserSelectedObject);
                this.splitContainer1.Panel1.Controls.Add(n.View);
            }
        }

        private void addEdgeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (graph.TaskBag.Count == 0)
            {
                MessageBox.Show("Select states by pressing SHIFT key. First source, then followed by the targets");
            }
            else
            {
                graph.IsDirty = true;
                Node N1 = graph.TaskBag[0];
                for (int i = 1; i < graph.TaskBag.Count; i++)
                {
                    Edge edge = new Edge(graph, N1, graph.TaskBag[i]);
                    graph.Edges.Add(edge);
                    propertyGrid.SelectedObject = edge;
                    this.splitContainer1.Panel1.Controls.Add(edge.View);
                }
                //edge.View.Refresh();
                graph.TaskBag.Clear();
            }
            
        }

        public bool IsDirty
        {
            get
            {
                return graph.IsDirty;
            }
        }

        private void resetSelectionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            graph.TaskBag.Clear();
        }

        private void addConsecutiveEdgesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (graph.TaskBag.Count == 0)
            {
                MessageBox.Show("Select states by pressing SHIFT key. First source, then followed by the targets");
            }
            else
            {
                graph.IsDirty = true;
                for (int i = 0; i < graph.TaskBag.Count-1; i++)
                {
                    Node N1 = graph.TaskBag[i];
                    Edge edge = new Edge(graph, N1, graph.TaskBag[i+1]);
                    graph.Edges.Add(edge);
                    propertyGrid.SelectedObject = edge;
                    this.splitContainer1.Panel1.Controls.Add(edge.View);
                }
                graph.TaskBag.Clear();
            }
        }

        public void SaveToFile(string sFilePath)
        {
            string oStateMachineXML = graph.ToXML();
            File.WriteAllText(sFilePath, oStateMachineXML);
        }

        public void GenerateTestHTML(string sOutputFilePath)
        {
            int nPos = sOutputFilePath.LastIndexOf(@"\"), nPos2 = sOutputFilePath.LastIndexOf(".");
            string xsltPath = string.Format("{0}JavaScript.xslt", frmMain.sThisAssemblyFolderPath),
                    sMachineDefintionJs = GetTransformedCode(xsltPath),
                    sHTMLContent = File.ReadAllText( string.Format("{0}HtmlTemplate.htm", frmMain.sThisAssemblyFolderPath ) ),
                    sOutputFolder = sOutputFilePath.Substring(0, nPos +1 ),
                    sFileName = sOutputFilePath.Substring( nPos + 1, nPos2 - nPos -1); 

            // Now run series of replacement for template variable 
            // The path to GI Script libraries (aka GrammarIntelliForJs.js & GrammarIntelliForJsUIHelper.js) is one level up
            // w.r.t where GIVisualEditor.exe
            string sScriptFolderPath = frmMain.sThisAssemblyFolderPath.Substring(0, frmMain.sThisAssemblyFolderPath.LastIndexOf(@"\")); // Don't include last \ because we want the previous one
            sScriptFolderPath = sScriptFolderPath.Substring(0, sScriptFolderPath.LastIndexOf(@"\") + 1);
            
            sHTMLContent = sHTMLContent.Replace("%%GI_FOLDER_PATH%%", sScriptFolderPath.Replace(@"\", "/"));
            sHTMLContent = sHTMLContent.Replace("%%GI_MACHINE_DEFINITION%%", sMachineDefintionJs);

            /* ISSUE: if panel has scroll bar then this approach will copy visible part to image file */
            Bitmap B = new Bitmap(splitContainer1.Panel1.Width, splitContainer1.Panel1.Height);
            splitContainer1.Panel1.DrawToBitmap(B, new Rectangle(0, 0, splitContainer1.Panel1.Width, splitContainer1.Panel1.Height));
            string sBitmapName = string.Format("{0}{1}.jpg", sOutputFolder, sFileName);
            B.Save(sBitmapName, System.Drawing.Imaging.ImageFormat.Jpeg);

            sHTMLContent = sHTMLContent.Replace("%%GRAMMAR_IMAGE_PATH%%", sFileName + ".jpg");

            File.WriteAllText(sOutputFilePath, sHTMLContent);

            /* time to launch the html */
            System.Diagnostics.Process.Start(sOutputFilePath);
            //System.Diagnostics.Process p = new System.Diagnostics.Process();
            //p.StartInfo.FileName = sHTMLContent;
            //p.StartInfo.UseShellExecute = true;
            //p.Start();
        }

        private string GetTransformedCode(string sTarnsformerXSLTPath)
        {
            string oStateMachineXML = graph.ToXML();
            XmlDocument xd = new XmlDocument();
            xd.LoadXml(oStateMachineXML);

            XslCompiledTransform xslt = new XslCompiledTransform();
            xslt.Load(sTarnsformerXSLTPath);
            StringBuilder sbResult = new StringBuilder();
            StringWriter stm = new StringWriter(sbResult);

            XsltArgumentList xsltArgs = new XsltArgumentList();
            xsltArgs.AddParam("MachineName", "", "oGIMachine");
            xslt.Transform(xd, xsltArgs, stm);

            return sbResult.ToString();

       }

        public void TransformToCode(string sTransformerXSLTPath, string sOutputFilePath)
        {
            if (File.Exists(sTransformerXSLTPath))
            {
                StreamWriter stm = new StreamWriter(sOutputFilePath);
                try
                {
                    stm.WriteLine(GetTransformedCode(sTransformerXSLTPath));
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error while transforming grammar to code.\n", ex.Message);
                }
                finally
                {
                    stm.Close();
                }
                MessageBox.Show("Done.");
            }
            else
            {
                Console.WriteLine(string.Format("Error: No transformation XSLT found for language '{0}'", sTransformerXSLTPath));
            }



        }

        private void deleteSelectedItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.propertyGrid.SelectedObject != null)
            {
                if (this.propertyGrid.SelectedObject is Node)
                {
                    try
                    {
                        // first get the index to this node
                        Node N = this.propertyGrid.SelectedObject as Node;
                        int index = graph.Nodes.FindIndex(X => X == N);
                        if (index >= 0)
                        {
                            // First remove from view
                            this.splitContainer1.Panel1.Controls.Remove(N.View);

                            // Now remove all egdes that are in-coming or out-going from this node
                            List<Edge> inoutEdges = graph.Edges.FindAll(E => E.FromState == N || E.ToState == N);
                            if (inoutEdges != null && inoutEdges.Count > 0)
                            {
                                foreach (Edge edge in inoutEdges)
                                {
                                    this.splitContainer1.Panel1.Controls.Remove(edge.View);
                                }
                            }

                            int nRemoved = graph.Edges.RemoveAll(E => E.FromState == N || E.ToState == N);

                            // Now remove this node from graph
                            graph.Nodes.Remove(N);
                            this.propertyGrid.SelectedObject = null;
                        }

                    }
                    catch (Exception ex)
                    {
                        // TODO: log error
                    }


                } else
                    if (this.propertyGrid.SelectedObject is Edge)
                    {
                        try
                        {
                            Edge E = this.propertyGrid.SelectedObject as Edge;
                            this.splitContainer1.Panel1.Controls.Remove(E.View);
                            this.graph.Edges.Remove(E);
                            this.propertyGrid.SelectedObject = null;
                        }
                        catch (Exception ex)
                        {
                            // TODO: log error
                        }
                    }
            }
        }

        private void autoArrangeViewToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Arranges the graph node/edges in containerPanel. Note: container may already contain UI elements
        /// </summary>
        /// <param name="g"></param>
        /// <param name="ContainerPanel"></param>
        private void AutoArrange(DirectedGraph g, Panel ContainerPanel)
        {



        }
    }
}
